#!/bin/env lua
if arg[0] == "convert-js.lua" then
	dofile("config.lua")
end

local USE_DIFF = USE_DIFF or not DONT_USE_DIFF

if USE_DIFF then
	dofile("diff.lua")
end

local function getline(f)
	local s = f:read()
	if not s then return s end
	local o = s

	while s:sub(-1, -1) == '\r' or s:sub(-1, -1) == '\n' do
		s = s:sub(1, -2)
	end
	s = s:gsub("//.*", "")
	s = s:gsub("^%s*", "")
	s = s:replace("coût de base", "coût en mana")

	-- s = s:gsub("^var ", "")

	local c = s:match("^var className%s*=%s*(\".*\");")
	if c then
		s = string.format("className = %s\n", c)
		return s
	end

	local name, v = s:match("(.*)%[i%] = (.*); i%+%+;?")

	if name then
		s = string.format("insert(%s, %s)", name, v:replace("[", "{"):replace("]", "}"))
	elseif s:match("^%s*%]%s*;") then
		s = "})"
	elseif
			s:match("[it] = 0;") or
			s:match("^var%s+.*") or
			s:match("[it]%s*%+%+;") or
			s:match("^treeStartStop")
			then

		s = ""
	else
		local cont
		name, cont = s:match("(.*)%[i%]%s*=%s*%[(.*)")
		if name then
			s = string.format("insert(%s, {", name)
			if cont then s = s .. cont end
		else
			local value = s:match("\"([^\"]*)\"%s*];?")
			if value then
				s = string.format("%q})", value)
			end
		end
		local value
	end
	-- s = s:gsub("= %[([^]]+)%]", "= {%1}")

	-- print(s)

	return s .."\n"
end

local function loadjsdata(src, tbl)
	f = io.open(src, "r")

	local line
	local result, error = load(function () line = getline(f) return line end, src)
	if not result then
		print(string.format("%q\nLoading error : %s\n", line or src, error))
	else
		setfenv(result, tbl)
		local res, error = pcall(result)
		if not res then
			print(string.format("Execution error : %s\n", error))
		end
	end
end



function conv_data(class, lang)
	local src = string.format(TEMP.."data-%s-%s.js", class:lower(), lang)
	local t = {
		tree = {},
		talent = {},
		rank = {},
		insert = table.insert,
		i = 0,
	}

	t.getTalentID = function (s)
		for i = 1, #t.talent do
			if t.talent[i][2] == s then
				return i
			end
		end
		error(string.format("unable to find talent \"%s\"", s))
	end

	loadjsdata(src, t)

	t.getTalentID = nil
	t.insert = nil
	t.class = class
	t.lang = lang

	return t
end

local ENTITIES = {
	["&nbsp;"] = " ",
	["&gt;"] = ">",
	["&lt;"] = "<",
}

local function cleanup_html(src)
	local result = src:replace("\r", ""):replace("\n", ""):replace("<br>", "\n")
	for entity, char in pairs(ENTITIES) do
		result = result:replace(entity, char)
	end
	local e = result:len()
	while e >= 1 and (" \t\n"):find(result:sub(e, e), 1, 1) do
		e = e - 1
	end
	return result:sub(1, e)
end

local function conv_tip(tip)
	if not tip:find("<", 1, 1) then
		return cleanup_html(tip)
	end
	local r = {}
	local p = 1
	local pspan = false
	local m = tip:len() + 1
	while p < m do
		local s1, e1, style, v1 = tip:find("<span style=([^>]+)>(.-)</span>", p)
		local s2, e2, v2 = tip:find("<span>([^<]+)</span>", p)
		local s3, e3 = tip:find("<[bB][rR]/?>", p)
		if not s1 and not s2 and not s3 then break end
		s1 = s1 or m
		s2 = s2 or m
		s3 = s3 or m

		if s1 < s2 and s1 < s3 then
			local t = style:match("text%-align:([^;]+)")
			if t and t == "right" then
				if #r == 0 then
					r[1] = { left = "" }
				end
				r[#r].right = cleanup_html(v1)
			else
				local s = cleanup_html(v1)
				local c = style:match("color:([^;]+)")
				if c then
					s = "|cff"..c..s.."|r"
				end
				r[#r + 1] = { left = s }
			end
			p = e1 + 1
			pspan = true
		elseif s2 < s3 then
			r[#r + 1] = { left = cleanup_html(v2) }
			pspan = true
			p = e2 + 1
		else
			v = tip:sub(p, s3 - 1)
			if v:len() > 0 or not pspan then
				r[#r + 1] = { left = cleanup_html(v) }
			end
			pspan = false
			p = e3 + 1
		end
	end
	local v = cleanup_html(tip:sub(p, -1))

	if v:len() then
		r[#r + 1] = { left = v }
	end

	for i = #r, 1, -1 do
		if not r[i].right and r[i].left == "" then
			r[i] = nil
		else
			break
		end
	end
	return r
end

local diff_results = {}

local function check_tip_ending(tips)
	local has_terminal_point
	for _, tip in ipairs(tips) do
		if type(tip) ~= "string" then return end
		if tip:sub(-1) == "." then
			has_terminal_point = true
			break
		end
	end
	if has_terminal_point then
		for i, tip in ipairs(tips) do
			if tip:sub(-1) ~= "." then
				tips[i] = tip.."."
			end
		end
	end
	if USE_DIFF then
		local fmt, results = build_diffs(unpack(tips))
		if fmt then
			tips.format = fmt
			for i = 1, #results do
				tips[i] = results[i]
			end
			diff_results.OK = (diff_results.OK or 0) + 1
		else
			results = results or "ERROR"
			diff_results[results] = (diff_results[results] or 0) + 1
		end
	end
end

local function clean_data(src, dst, table_cache)
	dst.class = src.class
	dst.className = src.className
	dst.trees = {}
	dst.talents = {}

	local talents, tindex, tree
	for index, talent in ipairs(src.talent) do
		local t, name = talent[1] + 1, talent[2]
		if tree ~= t then
			dst.trees[t] = cleanup_html(src.tree[t])
			tree = t
			tindex = 1
			talents = {}
			dst.talents[tree] = talents
		end
		talents[tindex] = { name = cleanup_html(name) }
		local tips = {}
		talents[tindex].tips = tips
		tindex = tindex + 1
		for t, tip in ipairs(src.rank[index]) do
			tips[t] = conv_tip(tip)
		end
		if #tips > talent[3] then
			print("ERROR : Too many tips", src.class, talent[2])
			for i = #tips, talent[3] + 1, -1 do
				tips[i] = nil
			end
		end
		local status, err = pcall(check_tip_ending, tips)
		if not status then
			print(src.class, t, tindex, err)
			os.exit(1)
		end
		if tips.format then
			local multiplier
			for i, results in ipairs(tips) do
				if #results > 1 then
					break
				end
				local v = results[1]
				if tostring(tonumber(v)) ~= v then
					break
				end
				if multiplier then
					if tonumber(v) ~= (multiplier * i) then
						multiplier = nil
						break
					end
				else
					multiplier = v / i
				end
			end
			if multiplier then
				for i in ipairs(tips) do
					tips[i] = nil
				end
				if multiplier ~= 1 then
					tips.multiplier = multiplier
				end
			else
				local mismatch
				local n = #tips
				for i, results in ipairs(tips) do
					local c = {}

					for _, v in ipairs(results) do
						if tostring(tonumber(v)) == v then
							c[#c + 1] = ("%s"):format(v)
						else
							c[#c + 1] = ("%q"):format(v)
						end
					end
					if #c > 1 then
						c = "{"..table.concat(c, ", ").."}"
					else
						c = c[1]
					end
					if c ~= tostring(i) then
						mismatch = true
						tips[i] = c
						if c:sub(1, 1) == "{" then
							table_cache[c] = (table_cache[c] or 0) + 1
						end
					else
						tips[i] = nil
					end
				end
				if mismatch then
					for i = 1, n do
						if not tips[i] then tips[i] = i end
					end
				end
			end
		else
			for t, tip in ipairs(tips) do
				if type(tip) == "table" then
					for line, s in ipairs(tip) do
						local c = s.right and ("{ left = %q, right = %q }"):format(s.left, s.right) or ("{ left = %q }"):format(s.left)
						tip[line] = c
						table_cache[c] = (table_cache[c] or 0) + 1
					end
				end
			end
		end
	end
end

local function write_data(src, f, table_cache)
	local w = function (s, ...)
		if s:find("%", 1, 1) and (...) then
			s = string.format(s, ...)
		end
		f:write("\t"..s.."\n")
	end
	w("data = Talented_Data.%s", src.class:upper())
	w("-- data.name = %q", src.className or nil)
	for i, name in ipairs(src.trees) do
		w("tree = data[%d]", i)
		w("tree.info.name = %q", name)
		for j, talent in ipairs(src.talents[i]) do
			w("talent = tree.talents[%d].info", j)
			w("talent.name = %q", talent.name)
			w("talent.tips = {")
			local tips = talent.tips
			if tips.format then
				w("\tformat = %q,", tips.format)
				if tips.multiplier then
					w("\tmultiplier = %s,", tips.multiplier)
				else
					for line, c in ipairs(tips) do
						if type(c) == "table" then
							for k, v in pairs(c) do
								print(k, v)
							end
						end
						if not table_cache[c] then
							w("\t%s, -- %d", c, line)
						else
							w("\ttable_cache_%s, -- %d", table_cache[c], line)
						end
					end
				end
			else
				for t, tip in ipairs(tips) do
					if type(tip) == "string" then
						w("\t%q, -- %d", tip, t)
					else
						w("\t{ -- %d", t)
						for line, c in ipairs(tip) do
							if not table_cache[c] then
								w("\t\t%s, -- %d", c, line)
							else
								w("\t\ttable_cache_%s,", table_cache[c], line)
							end
						end
						w("\t},")
					end
				end
			end
			w("}")
		end
	end
end

local function get_prev_cache_data(lmap)
	local result = {}
	local f = io.open(string.format(OUTPUT.."Talented_Data_%s.lua", lmap), "r")
	if f then
		local data = f:read("*a")
		f:close()
		for index, value in data:gmatch("local table_cache_(%d+) = ({[^}]+})") do
			result[tonumber(index)] = value
		end
	end
	return result
end

function convert_data(lang)
	local lmap = LANG_MAP[lang]

	print("Generating information for locale "..lmap.." ...") io.flush()
	local prev_cache_data = get_prev_cache_data(lmap)

	local conv = {}
	for class in pairs(CLASSES) do
		conv[class] = conv_data(class, lang)
	end
	local table_cache = {}
	local dst = {}
	for class in pairs(CLASSES) do
		local d = {}
		clean_data(conv[class], d, table_cache)
		dst[class] = d
	end

	print("LCS algorithm results :") io.flush()
	for name, value in pairs(diff_results) do
		print(string.format("\t%s : %d", name, value)) io.flush()
	end
	diff_results = {}

	local cache_data, last_index = {}, 0
	local table_count, unique_count, reused_count = 0, 0, 0
	local cache_indexes = {}
	for entry, count in pairs(table_cache) do
		table_count = table_count + count
		unique_count = unique_count + 1
		if count > 1 then
			local index
			for i, data in pairs(prev_cache_data) do
				if data == entry then
					index = i
					reused_count = reused_count + 1
					break
				end
			end
			if not index then
				local i = 1
				while cache_data[i] or prev_cache_data[i] do
					i = i + 1
				end
				index = i
			end
			-- assert(not cache_data[index], string.format("cache_data[%d] = %q, entry = %q", index, cache_data[index], entry))
			if cache_data[index] then
				print(index, type(cache_data[index]), cache_data[index])
			end
			assert(not prev_cache_data[index] or (prev_cache_data[index] == entry))
			cache_data[index] = entry
			cache_indexes[entry] = index
			if index > last_index then
				last_index = index
			end
		end
	end
	print(string.format("Table cache : %d total tables, %d distinct tables, %d duplicates and %d reused.", table_count, unique_count, #cache_data, reused_count)) io.flush()

	local f = io.open(string.format(OUTPUT.."Talented_Data_%s.lua", lmap), "w")
	f:write(string.format([[
-- Generated Data : Do Not Modify
if Talented_Data and GetLocale() == %q then
	local data, tree, talent
]], lmap))
	for index = 1, last_index do
		local entry = cache_data[index]
		if entry then
			f:write(string.format("\tlocal table_cache_%d = %s\n", index, entry))
		end
	end

	for _, info in pairs(dst) do
		write_data(info, f, cache_indexes)
	end

	f:write("end\n")

	print("Data written successfully.") io.flush()
end

if arg[0] == "convert-js.lua" then
	if #arg > 0 then
		for i = 1, #arg do
			convert_data(arg[i])
		end
	else
		for lang in pairs(LANG_MAP) do
			if not LANG_OVERRIDE[lang] then
				convert_data(lang)
			end
		end
	end
end
